import os
from selenium import webdriver
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver import ActionChains
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.core.os_manager import ChromeType
from apps.elements.serializers import ElementSerializers
from apps.envs.models import EnvPlant
from apps.elements.models import Element
from core.common import formatter_log
from core.step_model import StepModel
from core.common import single_replace_data, set_func_extract
from core.check import loop_assert_by_check_list


class SeleniumBaseAction:

    def __init__(self, params_class: object, case_logs_obj: object, host):
        # windows系统
        if os.name == 'nt1':
            service = ChromeService(ChromeDriverManager().install())
            self.driver = webdriver.Chrome(service=service)
        else:
            chrome_options = webdriver.ChromeOptions()
            chrome_options.add_argument('--no-sandbox')
            chrome_options.add_argument('--headless')
            self.driver = webdriver.Remote("http://39.101.141.162:4444/wd/hub", options=chrome_options)
        self.driver.maximize_window()
        self.params_class = params_class
        self.case_logs_obj = case_logs_obj
        self.host = host

    @staticmethod
    def __text(obj):
        return '【{}_{}_{}_{}】'.format(obj['plant_name'], obj['module_name'], obj['page_name'], obj['name'])

    def wait_element_located(self, obj: ElementSerializers, timeout=5):
        """
        等待元素被加载到Dom树中，并且可见
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return None
        """
        loc_method = obj['by']
        loc_value = obj['value']
        WebDriverWait(self.driver, timeout, 0.5).until(
            expected_conditions.visibility_of_element_located((loc_method, loc_value)))

    def find_element(self, obj: ElementSerializers, timeout=5):
        """
        查找元素
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return None
        """
        loc_method = obj['by']
        loc_value = obj['value']
        self.wait_element_located(obj, timeout)
        return self.driver.find_element(loc_method, loc_value)

    def get(self, obj: ElementSerializers):
        """
        访问某个网址
        obj: 元素对象, 必填
        return: None
        """
        url = self.host + obj['page_url']
        self.driver.get(url)
        self.case_logs_obj.logs.append(formatter_log('info', f'【访问网址：{url}】'))

    def click(self, obj: ElementSerializers, timeout=5):
        """
        点击元素
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        self.find_element(obj, timeout).click()
        self.case_logs_obj.logs.append(formatter_log('info', '【点击元素】' + self.__text(obj)))

    def submit(self, obj: ElementSerializers, timeout=5):
        """
        提交表单
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        self.find_element(obj, timeout).submit()
        self.case_logs_obj.logs.append(formatter_log('info', '【提交表单】' + self.__text(obj)))

    def is_selected(self, obj: ElementSerializers, timeout=5):
        """
        判断元素是否被选中
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: True 被选中 False 没有被选中
        """
        result = self.find_element(obj, timeout).is_selected()
        if result:
            self.case_logs_obj.logs.append(formatter_log('info', '【元素属于选中状态】' + self.__text(obj)))
        else:
            self.case_logs_obj.logs.append(formatter_log('info', '【元素属于非选中状态】' + self.__text(obj)))
        return result

    def send_keys(self, obj: ElementSerializers, value, timeout=5):
        """
        输入内容
        obj: 元素对象, 必填
        value: 输入得内容， 必填
        如果是键盘操作，value值参考下selenium官方文档
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        self.find_element(obj, timeout).send_keys(value)
        self.case_logs_obj.logs.append(formatter_log('info', f'【输入：{value}】' + self.__text(obj)))

    def get_text(self, obj: ElementSerializers, timeout=5):
        """
        获取元素文本得值
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: str 元素文本值
        """
        text = self.find_element(obj, timeout).text
        self.case_logs_obj.logs.append(formatter_log('info', f'【获取到元素文本值为：{text}】' + self.__text(obj)))
        return text

    def get_tag_name(self, obj: ElementSerializers, timeout=5):
        """
        获取元素标签名
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: str 元素标签名
        """
        tag_name = self.find_element(obj, timeout).tag_name
        self.case_logs_obj.logs.append(formatter_log('info', f'【获取到元素标签名为：{tag_name}】' + self.__text(obj)))
        return tag_name

    def get_attribute(self, obj: ElementSerializers, name, timeout=5):
        """
        获取元素的属性
        obj: 元素对象, 必填
        name: 元素属性名称, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: str 元素属性值
        """
        attr_value = self.find_element(obj, timeout).get_attribute(name)
        self.case_logs_obj.logs.append(formatter_log('info', f'【获取到元素属性名为{name}的属性值为：{attr_value}】' + self.__text(obj)))
        return attr_value

    def get_alert_text(self, obj: ElementSerializers, timeout=5):
        """
        获取Alert警告弹框的文本内容
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: str 弹框的文本内容
        """
        WebDriverWait(self.driver, timeout, 0.5).until(expected_conditions.alert_is_present())
        alter = self.driver.switch_to.alert
        text = alter.text
        self.case_logs_obj.logs.append(formatter_log('info', f'【获取到alert文本值为：{text}】' + self.__text(obj)))
        return text

    def alert_accept(self, obj: ElementSerializers, timeout=5):
        """
        点击Alert警告弹框的确认按钮
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        WebDriverWait(self.driver, timeout, 0.5).until(expected_conditions.alert_is_present())
        alter = self.driver.switch_to.alert
        alter.accept()
        self.case_logs_obj.logs.append(formatter_log('info', f'【点击alert确认按钮】' + self.__text(obj)))

    def alert_dismiss(self, obj: ElementSerializers, timeout=5):
        """
        点击Alert警告弹框的取消按钮
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        WebDriverWait(self.driver, timeout, 0.5).until(expected_conditions.alert_is_present())
        alter = self.driver.switch_to.alert
        alter.dismiss()
        self.case_logs_obj.logs.append(formatter_log('info', f'【点击alert取消按钮】' + self.__text(obj)))

    def alert_send_keys(self, obj: ElementSerializers, value, timeout=5):
        """
        在弹框中输入内容
        obj: 元素对象, 必填
        value: str, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        WebDriverWait(self.driver, timeout, 0.5).until(expected_conditions.alert_is_present())
        alter = self.driver.switch_to.alert
        alter.send_keys(value)
        self.case_logs_obj.logs.append(formatter_log('info', f'【在弹框中输入：{value}】' + self.__text(obj)))

    def add_cookie(self, obj: ElementSerializers, cookie_value: dict):
        """
        添加cookie
        obj: 元素对象, 必填
        cookie_value: cookie值， dict类型， 必填
        return: None
        """
        self.driver.add_cookie()
        self.case_logs_obj.logs.append(formatter_log('info', f'【添加cookie：{cookie_value}】' + self.__text(obj)))

    def get_cookie(self, obj: ElementSerializers, cookie_name: str):
        """
        获取cookie
        obj: 元素对象, 必填
        cookie_name: cookie名， str类型， 必填
        return: cookie的值
        """
        cookie = self.driver.get_cookie(cookie_name)
        self.case_logs_obj.logs.append(formatter_log('info', f'【获取cookie为：{cookie}】' + self.__text(obj)))
        return cookie

    def get_cookies(self, obj: ElementSerializers):
        """
        获取所有cookie
        obj: 元素对象, 必填
        return: list 所有cookie的值
        """
        cookies = self.driver.get_cookies()
        self.case_logs_obj.logs.append(formatter_log('info', f'【获取cookies为：{cookies}】' + self.__text(obj)))
        return cookies

    def delete_cookie(self, obj: ElementSerializers, cookie_name):
        """
        删除cookie
        obj: 元素对象, 必填
        cookie_name： str cookie名称
        return: None
        """
        self.driver.delete_cookie(cookie_name)
        self.case_logs_obj.logs.append(('info', f'【删除名称为{cookie_name}的cookie】' + self.__text(obj)))

    def delete_cookies(self, obj: ElementSerializers):
        """
        删除所有cookie
        obj: 元素对象, 必填
        return: None
        """
        self.driver.delete_all_cookies()
        self.case_logs_obj.logs.append(formatter_log('info', f'【删除所有cookies】' + self.__text(obj)))

    def get_title(self, obj: ElementSerializers):
        """
        获取当前页面的标题
        obj: 元素对象, 必填
        return: str 当前页面的标题
        """
        title = self.driver.title
        self.case_logs_obj.logs.append(formatter_log('info', f'【获取到页面标题为：{title}】' + self.__text(obj)))
        return title

    def get_current_url(self, obj: ElementSerializers):
        """
        获取当前页面的url
        obj: 元素对象, 必填
        return: str 当前页面的url
        """
        url = self.driver.current_url
        self.case_logs_obj.logs.append(formatter_log('info', f'【获取到页面url为：{url}】' + self.__text(obj)))
        return url

    def back(self, obj: ElementSerializers):
        """
        后退
        obj: 元素对象, 必填
        return: None
        """
        self.driver.back()
        self.case_logs_obj.logs.append(formatter_log('info', f'【后退】' + self.__text(obj)))

    def forward(self, obj: ElementSerializers):
        """
        前进
        obj: 元素对象, 必填
        return: None
        """
        self.driver.forward()
        self.case_logs_obj.logs.append(formatter_log('info', f'【前进】' + self.__text(obj)))

    def refresh(self, obj: ElementSerializers):
        """
        刷新
        obj: 元素对象, 必填
        return: None
        """
        self.driver.refresh()
        self.case_logs_obj.logs.append(formatter_log('info', f'【刷新】' + self.__text(obj)))

    def clear(self, obj: ElementSerializers, timeout=5):
        """
        清楚元素的文本内容
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        self.find_element(obj, timeout).clear()
        self.case_logs_obj.logs.append(formatter_log('info', f'【清除元素内容】' + self.__text(obj)))

    def move_to_element(self, obj: ElementSerializers, timeout=5):
        """
        鼠标悬停在某元素上
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        ActionChains(self.driver).move_to_element(self.find_element(obj, timeout)).perform()
        self.case_logs_obj.logs.append(formatter_log('info', f'【鼠标悬停' + self.__text(obj)))

    def double_click(self, obj: ElementSerializers, timeout=5):
        """
        鼠标双击在某元素上
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        ActionChains(self.driver).double_click(self.find_element(obj, timeout)).perform()
        self.case_logs_obj.logs.append(formatter_log('info', f'【鼠标双击】' + self.__text(obj)))

    def context_click(self, obj: ElementSerializers, timeout=5):
        """
        在某元素上点击鼠标右键
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        ActionChains(self.driver).context_click(self.find_element(obj, timeout)).perform()
        self.case_logs_obj.logs.append(formatter_log('info', f'【鼠标右键】' + self.__text(obj)))

    def drag_and_drop(self, obj1: ElementSerializers, obj2: ElementSerializers, timeout=5):
        """
        拖到元素1到元素2
        obj1: 元素对象, 必填
        obj2: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        ele1, ele2 = self.find_element(obj1, timeout), self.find_element(obj2, timeout)
        ActionChains(self.driver).drag_and_drop(ele1, ele2).perform()
        self.case_logs_obj.logs.append(formatter_log('info', f'【拖到元素】' + self.__text(obj1)) + self.__text(obj2))

    def switch_frame(self, obj: ElementSerializers, timeout=5):
        """
        进入frame
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        frame = self.find_element(obj, timeout)
        self.driver.switch_to.frame(frame)
        self.case_logs_obj.logs.append(formatter_log('info', f'【进入frame】' + self.__text(obj)))

    def exit_frame(self, obj: ElementSerializers, timeout=5):
        """
        退出frame
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        frame = self.find_element(obj, timeout)
        self.driver.switch_to.default_content(frame)
        self.case_logs_obj.logs.append(formatter_log('info', f'【退出frame】' + self.__text(obj)))

    def scroll_into_view(self, obj: ElementSerializers, timeout=5):
        """
        将元素移动到屏幕可见区域
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        self.driver.execute_script("arguments[0].scrollIntoView();", self.find_element(obj, timeout))
        self.case_logs_obj.logs.append(formatter_log('info', f'【移动元素到可见区域】' + self.__text(obj)))

    def select_by_label(self, obj: ElementSerializers, label_text, select=True, timeout=5):
        """
        通过下拉列表的文本选中某选项
        obj: 元素对象, 必填
        label_text: str, 下拉列表的文本值
        select: bool 默认是Ture，选中， False： 取消选中
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        if select:
            Select(self.find_element(obj, timeout)).select_by_visible_text(label_text)
            self.case_logs_obj.logs.append(formatter_log('info', f'【选中下拉列表的文本值为{label_text}的选项】' + self.__text(obj)))
        else:
            Select(self.find_element(obj, timeout)).deselect_by_visible_text(label_text)
            self.case_logs_obj.logs.append(formatter_log('info', f'【取消选中下拉列表的文本值为{label_text}的选项】' + self.__text(obj)))

    def select_by_value(self, obj: ElementSerializers, value, select=True, timeout=5):
        """
        通过下拉列表的值选中某选项
        obj: 元素对象, 必填
        value: str, 下拉列表的值
        select: bool 默认是Ture，选中， False： 取消选中
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        if select:
            Select(self.find_element(obj, timeout)).select_by_value(value)
            self.case_logs_obj.logs.append(formatter_log('info', f'【选中下拉列表的值为{value}的选项】' + self.__text(obj)))
        else:
            Select(self.find_element(obj, timeout)).deselect_by_value(value)
            self.case_logs_obj.logs.append(formatter_log('info', f'【取消选中下拉列表的值为{value}的选项】' + self.__text(obj)))

    def select_by_index(self, obj: ElementSerializers, index, select=True, timeout=5):
        """
        通过下拉列表的索引选中某选项
        obj: 元素对象, 必填
        index: int, 索引值
        select: bool 默认是Ture，选中， False： 取消选中
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        if select:
            Select(self.find_element(obj, timeout)).select_by_index(index)
            self.case_logs_obj.logs.append(formatter_log('info', f'【选中下拉列表的索引为{index}的选项】' + self.__text(obj)))
        else:
            Select(self.find_element(obj, timeout)).deselect_by_index(index)
            self.case_logs_obj.logs.append(formatter_log('info', f'【取消选中下拉列表的索引为{index}的选项】' + self.__text(obj)))

    def deselect_all(self, obj: ElementSerializers, timeout=5):
        """
        取消所有选中项
        obj: 元素对象, 必填
        timeout: 超时时间, int, 非必填，默认5s
        return: None
        """
        Select(self.find_element(obj, timeout)).deselect_all()
        self.case_logs_obj.logs.append(formatter_log('info', f'【取消所有选中项】' + self.__text(obj)))


selenium_actions = [{'id': key, 'name': key}
                    for key, value in SeleniumBaseAction.__dict__.items()
                    if not key.startswith('_')]

selenium_func_map = {key: value for key, value in SeleniumBaseAction.__dict__.items()
                     if not key.startswith('_')}

selenium_func_doc = {key: [new_str.strip() for new_str in value.__doc__.strip().split('\n')]
                     for key, value in SeleniumBaseAction.__dict__.items()
                     if not key.startswith('_')}


def get_obj_params_value(element_id, env_id):
    data = ElementSerializers(Element.objects.all().get(id=element_id)).data
    env_plant_obj = EnvPlant.objects.all().get(env=env_id, plant=data.get('plant_id'), is_delete=False)
    return env_plant_obj.host, data


def run_step_web_ui(manager_obj, steps: list, step_key, env_id, one_step_data: StepModel,
                    params_class, case_logs_obj):
    params_list, host = list(), None
    for params_obj in one_step_data.params:
        if params_obj.type == 'Obj':
            host, obj = get_obj_params_value(params_obj.value, env_id)
            params_list.append(obj)
        elif params_obj.type == 'Dynamic':
            params_list.append(single_replace_data(params_class, case_logs_obj, params_obj.value))
        else:
            params_list.append(params_obj.value)

    if host not in manager_obj.driver_manager:
        manager_obj.driver_manager[host] = SeleniumBaseAction(params_class, case_logs_obj, host)

    return_value = selenium_func_map[step_key](manager_obj.driver_manager[host], *params_list)
    set_func_extract(return_value, one_step_data.extract, case_logs_obj, params_class)
    # 执行步骤的断言参数,如果步骤中有断言数据则忽略接口的断言数据
    loop_assert_by_check_list(one_step_data.check, params_class, case_logs_obj)





